fix(ui): guard MessageCard._updateWidthLimit against unlaid RenderBox#2702
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThe pull request refactors the ChangesAttachment Width Measurement Safety
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2702 +/- ##
==========================================
+ Coverage 65.38% 65.66% +0.27%
==========================================
Files 423 423
Lines 26665 26666 +1
==========================================
+ Hits 17435 17509 +74
+ Misses 9230 9157 -73 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
4558737 to
038045e
Compare
…id RenderBox `_MessageCardState._updateWidthLimit` is scheduled via `WidgetsBinding.instance.addPostFrameCallback` from `didChangeDependencies`, and reads `renderBox.size.width` when it fires. If the attachments subtree has been detached from the tree between scheduling and firing (e.g. the parent removed the message because the list was rebuilt, the chat was disposed, or the attachment was deleted), `RenderBox.size` throws `StateError: RenderBox was not laid out` per the framework contract. The throw is caught by `FlutterError.onError`, so users see a dropped frame rather than a crash — but Sentry still records it as a fatal error. In one production Flutter app the two corresponding Sentry issues have generated 1500+ events across 1100+ unique users in a single 2-week release. The error appears under any view path that has chat messages with attachments (chat, groupPost, splash, root). Fix is a `hasSize` guard before reading `.size`, plus moving the existing `mounted` check to the top of the function for symmetry (it was previously only checked before `setState`, so we still touched the unmounted render object first). The render-box-was-not-laid-out error is the canonical signature of this mistake in Flutter; the `hasSize` check is exactly what the framework docs recommend for this case.
038045e to
a52304a
Compare
Resolves SPL conflicts in scrollable_positioned_list and positioned_list: - Take master's preserve-pixels `_updateFirstVisibleItemIfNeeded` (PR #2703) - Take master's empty-list `_centerSliverPadding` guard - Take master's RTL `_axisDirection`-based `_updatePositions` branching - Take master's `_resolveLeadingEndPaddingAdjust` `index != 0` check - Drop orphaned `message_card_test.dart` (widget deleted in v10 design refresh); master's MessageCard fix from #2702 ported to StreamMessageContent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Submit a pull request
Linear: FLU-
Github Issue: #
CLA
Description of the pull request
Bug
_MessageCardState._updateWidthLimitis scheduled viaWidgetsBinding.instance.addPostFrameCallbackfromdidChangeDependencies, and readsrenderBox.size.widthwhen it fires:If the attachments subtree has been detached from the tree between scheduling and firing (e.g. the parent rebuilt the list, the chat was disposed, the attachment was deleted, the user navigated away), then per Flutter's contract
RenderBox.sizethrowsStateError: RenderBox was not laid out:The throw is caught by
FlutterError.onError, so the user sees a dropped frame rather than a crash. But it surfaces as a fatal error in crash reporters like Sentry. In a production Flutter app pinned tostream_chat_flutter: ^9.24.0, the two Sentry issues corresponding to this code path have generated over 1500 events across 1100+ unique users in a single 2-week release — the loudest non-network issue in the audit.Sample symbolicated stack:
Fix
hasSizeguard before readingrenderBox.size— exactly what the framework docs recommend for the "read size after a frame" pattern.mountedcheck from "beforesetState" to the top of the function. Previously, on an unmounted widget we still touched the (possibly unlaid) render object — only thesetStatewas skipped. The earlier check is symmetrical and slightly cheaper.Diff:
Testing
This is a defensive guard against a framework-level race that fires intermittently on real user devices and is hard to reproduce locally. The guard preserves the existing happy-path behavior (when the render object is laid out, width is read and applied identically to before) and short-circuits cleanly when it's not.
Screenshots / Videos
Not applicable — this fix removes a race-condition error log, no visual change.
Summary by CodeRabbit